package sequence

import (
	
	

	
	
)

const (
	defaultSelfMessageWidth   = 4
	defaultMessageSpacing     = 1
	defaultParticipantSpacing = 5
	boxPaddingLeftRight       = 2
	minBoxWidth               = 3
	boxBorderWidth            = 2
	labelLeftMargin           = 2
	labelBufferSpace          = 10
)

type diagramLayout struct {
	participantWidths  []int
	participantCenters []int
	totalWidth         int
	messageSpacing     int
	selfMessageWidth   int
}

func calculateLayout( *SequenceDiagram,  *diagram.Config) *diagramLayout {
	 := .SequenceParticipantSpacing
	if  <= 0 {
		 = defaultParticipantSpacing
	}

	 := make([]int, len(.Participants))
	for ,  := range .Participants {
		 := runewidth.StringWidth(.Label) + boxPaddingLeftRight
		if  < minBoxWidth {
			 = minBoxWidth
		}
		[] = 
	}

	 := make([]int, len(.Participants))
	 := 0
	for  := range .Participants {
		 := [] + boxBorderWidth
		if  == 0 {
			[] =  / 2
			 = 
		} else {
			 += 
			[] =  + /2
			 += 
		}
	}

	 := len(.Participants) - 1
	 := [] + ([]+boxBorderWidth)/2

	 := .SequenceMessageSpacing
	if  <= 0 {
		 = defaultMessageSpacing
	}
	 := .SequenceSelfMessageWidth
	if  <= 0 {
		 = defaultSelfMessageWidth
	}

	return &diagramLayout{
		participantWidths:  ,
		participantCenters: ,
		totalWidth:         ,
		messageSpacing:     ,
		selfMessageWidth:   ,
	}
}

func ( *SequenceDiagram,  *diagram.Config) (string, error) {
	if  == nil || len(.Participants) == 0 {
		return "", fmt.Errorf("no participants")
	}
	if  == nil {
		 = diagram.DefaultConfig()
	}

	 := Unicode
	if .UseAscii {
		 = ASCII
	}

	 := calculateLayout(, )
	var  []string

	 = append(, buildLine(.Participants, , func( int) string {
		return string(.TopLeft) + strings.Repeat(string(.Horizontal), .participantWidths[]) + string(.TopRight)
	}))

	 = append(, buildLine(.Participants, , func( int) string {
		 := .participantWidths[]
		 := runewidth.StringWidth(.Participants[].Label)
		 := ( - ) / 2
		return string(.Vertical) + strings.Repeat(" ", ) + .Participants[].Label +
			strings.Repeat(" ", --) + string(.Vertical)
	}))

	 = append(, buildLine(.Participants, , func( int) string {
		 := .participantWidths[]
		return string(.BottomLeft) + strings.Repeat(string(.Horizontal), /2) +
			string(.TeeDown) + strings.Repeat(string(.Horizontal), -/2-1) +
			string(.BottomRight)
	}))

	for ,  := range .Messages {
		for  := 0;  < .messageSpacing; ++ {
			 = append(, buildLifeline(, ))
		}

		if .From == .To {
			 = append(, renderSelfMessage(, , )...)
		} else {
			 = append(, renderMessage(, , )...)
		}
	}

	 = append(, buildLifeline(, ))
	return strings.Join(, "\n") + "\n", nil
}

func buildLine( []*Participant,  *diagramLayout,  func(int) string) string {
	var  strings.Builder
	for  := range  {
		 := .participantWidths[] + boxBorderWidth
		 := .participantCenters[] - /2

		 :=  - len([]rune(.String()))
		if  > 0 {
			.WriteString(strings.Repeat(" ", ))
		}
		.WriteString(())
	}
	return .String()
}

func buildLifeline( *diagramLayout,  BoxChars) string {
	 := make([]rune, .totalWidth+1)
	for  := range  {
		[] = ' '
	}
	for ,  := range .participantCenters {
		if  < len() {
			[] = .Vertical
		}
	}
	return strings.TrimRight(string(), " ")
}

func renderMessage( *Message,  *diagramLayout,  BoxChars) []string {
	var  []string
	,  := .participantCenters[.From.Index], .participantCenters[.To.Index]

	 := .Label
	if .Number > 0 {
		 = fmt.Sprintf("%d. %s", .Number, .Label)
	}

	if  != "" {
		 := min(, ) + labelLeftMargin
		 := runewidth.StringWidth()
		 := max(.totalWidth, +) + labelBufferSpace
		 := []rune(buildLifeline(, ))
		if len() <  {
			 := make([]rune, -len())
			for  := range  {
				[] = ' '
			}
			 = append(, ...)
		}

		 := 
		for ,  := range  {
			if  < len() {
				[] = 
				++
			}
		}
		 = append(, strings.TrimRight(string(), " "))
	}

	 := []rune(buildLifeline(, ))
	 := .SolidLine
	if .ArrowType == DottedArrow {
		 = .DottedLine
	}

	if  <  {
		[] = .TeeRight
		for  :=  + 1;  < ; ++ {
			[] = 
		}
		[-1] = .ArrowRight
		[] = .Vertical
	} else {
		[] = .Vertical
		[+1] = .ArrowLeft
		for  :=  + 2;  < ; ++ {
			[] = 
		}
		[] = .TeeLeft
	}
	 = append(, strings.TrimRight(string(), " "))
	return 
}

func renderSelfMessage( *Message,  *diagramLayout,  BoxChars) []string {
	var  []string
	 := .participantCenters[.From.Index]
	 := .selfMessageWidth

	 := func( string) []rune {
		 := .totalWidth +  + 1
		 := []rune()
		if len() <  {
			 := make([]rune, -len())
			for  := range  {
				[] = ' '
			}
			 = append(, ...)
		}
		return 
	}

	 := .Label
	if .Number > 0 {
		 = fmt.Sprintf("%d. %s", .Number, .Label)
	}

	if  != "" {
		 := (buildLifeline(, ))
		 :=  + labelLeftMargin
		 := runewidth.StringWidth()
		 :=  +  + labelBufferSpace
		if len() <  {
			 := make([]rune, -len())
			for  := range  {
				[] = ' '
			}
			 = append(, ...)
		}
		 := 
		for ,  := range  {
			if  < len() {
				[] = 
				++
			}
		}
		 = append(, strings.TrimRight(string(), " "))
	}

	 := (buildLifeline(, ))
	[] = .TeeRight
	for  := 1;  < ; ++ {
		[+] = .Horizontal
	}
	[+-1] = .SelfTopRight
	 = append(, strings.TrimRight(string(), " "))

	 := (buildLifeline(, ))
	[+-1] = .Vertical
	 = append(, strings.TrimRight(string(), " "))

	 := (buildLifeline(, ))
	[] = .Vertical
	[+1] = .ArrowLeft
	for  := 2;  < -1; ++ {
		[+] = .Horizontal
	}
	[+-1] = .SelfBottom
	 = append(, strings.TrimRight(string(), " "))

	return 
}